

<template>
  <div class="list" ref="list">
    <ul :style="ulStyle">
      <li v-for="(item) in list" :key="item[indexKey]">
        {{ item[showItem] ?item[showItem] : item.value  }}
        
      </li>
    </ul>
    <div class="mask"></div>
    <div class="hairline"></div>
  </div>
</template>
<script>
import {
  getClient,
  START_EVENT,
  MOVE_EVENT,
  END_EVENT,
  isPC,
} from "../util/util.js";
const LIMIT_TIME = 300;
const LIMIT_DISTANCE = 15;
const IS_PC = isPC();
const DEFAULT_DURATION = 300;
export default {
  data() {
    return {
      defaultIndex:0,
      ulStyle: {
        transform: `translate3d(0px, 0px, 0px)`,
        transitionDuration: `0ms`,
        transitionProperty: `none`,
        lineHeight: `${this.itemHeight}px`,
      },
    };
  },
  props: {
    showItem:{
      type:String,
      default:''
    },
    value:{
      type:String,
      default:''
    },
    indexKey:{
      type:String,
      default:''
    },
    list:{
      type:Array,
      default:()=>[]
    },
    boxHeight: {
      type: Number,
      default: 220,
    },
    itemHeight: {
      type: Number,
      default: 44,
    },
    type: {
      type: String,
      default: "",
    },
  },
  computed: {
    count() {
      return this.list.length;
    },
    getRoNumber() {
      return Math.floor(4 / 2);
    },
  },
  methods: {
    init() {
      if (this.type === "dynamic") {
        this.defaultIndex =
          this.list.findIndex((item) => {
            return item[this.indexKey] === this.value;
          }) > -1
            ? this.list.findIndex((item) => {
                return item[this.indexKey] === this.value;
              })
            : -1;
      } else {
        this.defaultIndex =
          this.list.findIndex((item) => {
            return item.value === this.value;
          }) > -1
            ? this.list.findIndex((item) => {
                return item.value === this.value;
              })
            : -1;
      }
      this.setTop(this.defaultIndex,true);
      const halfBox = (this.boxHeight - this.itemHeight) / 2;
      this.bottom = halfBox + this.itemHeight;
      this.top = halfBox - this.count * this.itemHeight;
    },
    // 根据index 设置滚动位置
    setTop(index = 0,flag) {
      const { boxHeight, itemHeight } = this;
      this.startTop = (boxHeight - itemHeight) / 2 - index * itemHeight;
      this.ulStyle.transform = `translate3d(0px, ${this.startTop}px, 0px)`;
      this.selectIndex = index;
      if (!flag) {
        this.change();
      }
    },
    handleStart(e) {
      this.distStartTop = getClient(e).y;
      this.touchStartTime = Date.now();
      // ----
      this.startY = getClient(e).y;
      this.momentumTop = this.startTop;
      this.ulStyle.transitionDuration = `0ms`;
      this.ulStyle.transitionProperty = `none`;
      if (IS_PC) {
        document.addEventListener(MOVE_EVENT, this.handleMove, false);
        document.addEventListener(END_EVENT, this.handleEnd, false);
      }
    },
    handleMove(e) {
      e.preventDefault();
      e.stopPropagation();
      this.disY = getClient(e).y - this.startY;
      this.startY = getClient(e).y;
      if (this.startTop >= this.bottom) {
        this.startTop = this.bottom;
      } else if (this.startTop <= this.top) {
        this.startTop = this.top;
      } else {
        this.startTop += this.disY;
      }
      this.ulStyle.transform = `translate3d(0px, ${this.startTop}px, 0px)`;
      const now = Date.now();
      if (now - this.touchStartTime > LIMIT_TIME) {
        this.touchStartTime = now;
        this.momentumTop = this.startTop;
      }
    },
    handleEnd() {
      if (IS_PC) {
        document.removeEventListener(MOVE_EVENT, this.handleMove, false);
        document.removeEventListener(END_EVENT, this.handleEnd, false);
      }
      const distance = this.startTop - this.momentumTop;
      const duration = Date.now() - this.touchStartTime;
      const allowMomentum =
        duration < LIMIT_TIME && Math.abs(distance) > LIMIT_DISTANCE;
      if (allowMomentum) {
        this.toMove(distance, duration);
      } else {
        this.setTranfromTop();
      }
    },
    setTranfromTop() {
      this.ulStyle.transitionProperty = `all`;
      this.ulStyle.transitionDuration = `${DEFAULT_DURATION}ms`;
      if (this.startTop >= this.bottom - this.itemHeight) {
        this.setTop();
      } else if (this.startTop <= this.top + this.itemHeight) {
        this.setTop(this.count - 1);
      } else {
        let index = Math.round(this.startTop / this.itemHeight);
        this.startTop = index * this.itemHeight;
        if (this.startTop > this.bottom) {
          this.startTop = this.bottom - this.itemHeight;
          index = -this.getRoNumber;
        } else if (this.startTop < this.top) {
          this.startTop = this.top + this.itemHeight;
          index = this.count + 1;
        }
        this.ulStyle.transform = `translate3d(0px, ${this.startTop}px, 0px)`;
        index = this.getRoNumber - index;
        if (this.selectIndex !== index) {
          this.selectIndex = index;
          this.change();
        }
      }
    },
    toMove(distance, duration) {
      const speed = Math.abs(distance / duration);
      distance = this.startTop + (speed / 0.002) * (distance < 0 ? -1 : 1);
      this.ulStyle.transitionProperty = `all`;
      this.ulStyle.transitionDuration = `1000ms`;
      this.setTop(
        Math.min(
          Math.max(Math.round(-distance / this.itemHeight), 0),
          this.count - 1
        )
      );
    },
    change() {
      this.$emit("change", this.list[this.selectIndex])
      
    },
    mousewheel(e) {
      e.preventDefault();
      e.stopPropagation();
      this.ulStyle.transitionDuration = `0ms`;
      this.ulStyle.transitionProperty = `none`;
      const { deltaX, deltaY } = e;
      if (Math.abs(deltaX) < Math.abs(deltaY)) {
        this.startTop = this.startTop - deltaY;
        let b = this.bottom - this.itemHeight;
        let t = this.top + this.itemHeight;
        let shouldMove = true;
        if (this.startTop > b) {
          this.startTop = b;
          shouldMove = false;
        } else if (this.startTop < t) {
          this.startTop = t;
          shouldMove = false;
        }
        this.ulStyle.transform = `translate3d(0px, ${this.startTop}px, 0px)`;
        if (shouldMove) {
          clearInterval(this.wheelTimer);
          this.wheelTimer = setTimeout(this.setTranfromTop, 100);
        }
      }
    },
  },
  created() {},
  mounted() {
    this.init();
    // 监听开始事件
    this.$el.addEventListener(START_EVENT, this.handleStart, false);
    if (IS_PC) {
      this.$el.addEventListener("wheel", this.mousewheel, false);
    } else {
      this.$el.addEventListener(MOVE_EVENT, this.handleMove, false);
      this.$el.addEventListener(END_EVENT, this.handleEnd, false);
    }
  },
  watch: {
    list() {
      this.init();
    },
    value() {
      this.init();
    },
  },
  beforeDestroy() {
    this.$el.removeEventListener(START_EVENT, this.handleStart, false);
    if (IS_PC) {
      this.$el.removeEventListener("wheel", this.mousewheel, false);
      this.$el.removeEventListener(MOVE_EVENT, this.handleMove, false);
      this.$el.removeEventListener(END_EVENT, this.handleEnd, false);
    }
  },
};
</script>
<style lang="less" scoped>
.list {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 220px;
  overflow: hidden;
  position: relative;
  ul {
    margin: 0;
    padding: 0;
    transition-timing-function: cubic-bezier(0.23, 1, 0.68, 1);
    line-height: 44px;
  }
  li {
    margin: 0;
    padding: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    padding: 0 5px;
    text-align: center;
    color: #000;
  }
  .mask {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
    width: 100%;
    height: 100%;

    background-image: linear-gradient(
        180deg,
        hsla(0, 0%, 100%, 0.9),
        hsla(0, 0%, 100%, 0.4)
      ),
      linear-gradient(0deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.4));
    background-repeat: no-repeat;
    background-position: top, bottom;
    backface-visibility: hidden;
    pointer-events: none;
    background-size: 100% 88px;
  }
  .hairline {
    position: absolute;
    top: 50%;
    left: 0;
    z-index: 3;
    width: 100%;
    transform: translateY(-50%);
    pointer-events: none;
    height: 44px;
    &::after {
      position: absolute;
      box-sizing: border-box;
      content: " ";
      pointer-events: none;
      top: -50%;
      right: -50%;
      bottom: -50%;
      left: -50%;
      border: 0 solid #ebedf0;
      -webkit-transform: scale(0.5);
      transform: scale(0.5);
      border-width: 1px 0;
    }
  }
}
</style>